package top.codedance.iotp.server.net;

import cd.io.netty.buffer.PooledByteBufAllocator;
import top.codedance.iotp.server.ServerApplication;
import top.codedance.iotp.server.net.handle.configuration.WsRouteConfig;
import top.codedance.iotp.server.net.handle.entity.ReceiverQueueEntity;
import top.codedance.iotp.server.net.handle.listenner.WsConnectionListener;
import top.codedance.iotp.server.net.handle.subhandle.controller.WsController;
import top.codedance.iotp.server.net.initializer.WsPipelineInitializer;
import cd.io.netty.bootstrap.ServerBootstrap;
import cd.io.netty.channel.ChannelFuture;
import cd.io.netty.channel.ChannelOption;
import cd.io.netty.channel.EventLoopGroup;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.nio.NioServerSocketChannel;
import cd.io.netty.handler.logging.LogLevel;
import cd.io.netty.handler.logging.LoggingHandler;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;

public class WsServer implements Server {
    private static Logger logger = ServerApplication.getLogger(WsServer.class);

    public static volatile Queue<ReceiverQueueEntity> localTaskReceiverQueue = new ConcurrentLinkedDeque<>(); //针对本地任务的回复对列
    public static volatile Map<Short, Set<String>> localTaskClients = new HashMap<>(); //本地任务监听客户端
    public static final Map<String, WsController> websocketRoutes = new HashMap<>(); //websocket路由
    private int port = 10013;
    private String ip = "0.0.0.0";

    public WsServer(String ip, int port){
        this.ip = ip;
        this.port = port;
    }

    public void run() {
        websocketRoutes.putAll(WsRouteConfig.getConfig());
        EventLoopGroup boss = new NioEventLoopGroup(ServerApplication.ioThreads);
        EventLoopGroup httpWorker = new NioEventLoopGroup(ServerApplication.workerThreads);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            ServerBootstrap group = bootstrap.group(boss, httpWorker);
            String logLevelArg = ServerApplication.commandCli.getArg("logLevel", "OFF");
            if(Arrays.asList(LogLevel.values(), String.class).contains(logLevelArg)) {
                group.handler(new LoggingHandler(LogLevel.valueOf(logLevelArg)));
            }
            group.channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 512)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.SO_RCVBUF, 128 * 1024)
                    .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
                    .childHandler(new WsPipelineInitializer());

            ChannelFuture channelFuture;
            if(!ServerApplication.enableIpBindPort || "0.0.0.0".equals(ip)){
                channelFuture = bootstrap.bind(port).sync();
            }else{
                channelFuture = bootstrap.bind(ip, port).sync();
            }

            if (channelFuture.isDone()) {
                ServerApplication.syslogInfo(String.format("Websocket server bind port %s success", port));
            }
            channelFuture.addListener(new WsConnectionListener());
            /**CloseFuture异步方式关闭*/
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.debug(e.getMessage());
        } finally {
            boss.shutdownGracefully();
            httpWorker.shutdownGracefully();
        }
    }
}
